/* semanticValidationStructures.h
 *
 * Este archivo contiene las estructuras para hacer validacion semantica y es 
 * utilizado por coffeec.atg y tambien por la maquina virtual SinCrema.cpp.
 *
 */

#include <list>
#include <vector>
#include <stdio.h>
#include <fstream>

#define BASE_CONST 6000
#define CONST_JUNK_SIZE 5000

#define BASE_MEM 26000
#define MEM_JUNK_SIZE 500

#define BASE_TEMP 0
#define TEMP_JUNK_SIZE 1500 

using namespace std;

/* class queue
 *
 * Esta clase implementa una cola tradicional (FIFO) de elementos definidos por 
 * el usuario (puede aceptar cualquier tipo de variables, estructuras y objetos).
 *
 * Sus metodos son: 
 * clear()				- limpia la cola completa
 * isEmpty()			- regresa un booleano seteado en true si esta vacia y false si no lo esta.
 * front()				- regresa una copia del primer elemento a salir sin sacarlo
 * dequeue()			- regresa el primer elemento sacandolo de la cola
 * insertIntoQueue()	- inserta un elemento
 *
 */

template<class T>

class queue {
	public:
	   queue() { }
	
	   void clear() {
		  lst.clear();
	   }
	
	   bool isEmpty() const {
		  return lst.empty();
	   }
	
	   T& front() {
		  return lst.front();
	   }
	
	   T dequeue() {
		  T el = lst.front();
		  lst.pop_front();
		  return el;
	   }
	
	   void enqueue(const T& el) {
		  lst.push_back(el);
	   }

		int getSize() {
			if(!isEmpty()) {
				return lst.size();
			} else {
				return 0;
			}
		}

		void insertIntoQueue(queue<T> *q) {
			queue<T> tQ;
			T el;
			while (!isEmpty()) {
				el = dequeue();
				q->enqueue(el);
				tQ.enqueue(el);
			}
			while (!tQ.isEmpty()) {
				el = tQ.dequeue();
				enqueue(el);
			}
		}
	
	private:
	   std::list<T> lst;
};

/* class stack
 *
 * Esta clase implementa una pila tradicional (FILO) de elementos definidos por 
 * el usuario (puede aceptar cualquier tipo de variables, estructuras y objetos).
 *
 * Sus metodos son: 
 * clear()				- limpia la pila completa
 * isEmpty()			- regresa un booleano seteado en true si esta vacia y false si no lo esta.
 * topEL()				- regresa una copia del primer elemento a salir sin sacarlo
 * pop()				- regresa el primer elemento sacandolo de la pila
 * push()				- inserta un elemento
 *
 */

template<class T>

class stack {
	public:
	   stack(){
	   }
	
	   void clear(){
		  lst.clear();
	   }
	
	   bool isEmpty() const{
		  return lst.empty();
	   }
	
	   T& topEL(){
		  return lst.back();
	   }
	
	   T pop(){
		  T el = lst.back();
		  lst.pop_back();
		  return el;
	   }
	
	   void push(const T& el){
		  lst.push_back(el);
	   }
	
	private:
	   std::list<T> lst;   
};

/* class constant variables table
 *
 * Esta clase implementa la tabla de constantes del compilador. Trabaja con 4
 * vectores, uno para cada tipo de datos, y va almacenando en estos estructuras
 * que posee el valor de la constante y su direccion en memoria.
 *
 * Sus contenedores de datos son:
 * constInt		- una estructura que almacena una constante entera
 * constFloat	- una estructura que almacena una constante flotante
 * constBool	- una estructura que almacena una constante booleana
 * constChar	- una estructura que almacena una constante char
 * constVectorInt   - un vector de estructuras constInt
 * constVectorFloat - un vector de estructuras constFloat
 * constVectorBool  - un vector de estructuras constBool
 * constVectorChar  - un vector de estructuras constChar
 *
 * Sus metodos son: 
 * constVariableTable()	- el constructor de la clase, se puede invocar vacío 
 *	  o con el numero de memoria base y el tamano de cada trozo de memoria (de cada
 *	  tipo).
 * insert()	- inserta la estructura de datos que recibe y dependiendo del tipo de
 *	  estructura que sea, la guarda en el vector de su tipo correspondiente.
 * saveTable() - vacia el contenido de cada uno de los vectores en un archivo de
 *	  nombre ConstantVarTable.txt ordenados por direccion de memoria.
 * reconstructConstTable() - a partir del archivo ConstantVarTable.txt recrea la
 *	  memoria de constantes cargandola a memoria de ejecusion
 * getConstVectorInt() - retorna el vector de enteros constVectorInt
 * getConstVectorFloat() - retorna el vector de flotantes constVectorFloat
 * getConstVectorBool() - retorna el vector de booleanos constVectorBool
 * getConstVectorChar() - retorna el vector de chars constVectorChar
 *
 */

class constVariableTable{
	struct constInt{
		int value;
		int direction;
	};

	struct constFloat{
		float value;
		int direction;
	};
	
	struct constBool{
		bool value;
		int direction;
	};

	struct constChar{
		char value;
		int direction;
	};
	
	vector<constInt> constVectorInt;
	vector<constInt>::iterator itInt;

	vector<constFloat> constVectorFloat;
	vector<constFloat>::iterator itFloat;

	vector<constBool> constVectorBool;
	vector<constBool>::iterator itBool;

	vector<constChar> constVectorChar;
	vector<constChar>::iterator itChar;

	int baseDirection, junkSize;
	int counters[4];

	public:
	constVariableTable(int base_const, int const_junk_size){ 
		itInt = constVectorInt.begin();
		itFloat = constVectorFloat.begin();
		itBool = constVectorBool.begin();
		itChar = constVectorChar.begin();
		junkSize = const_junk_size;
		baseDirection = counters[0] = base_const;
		counters[1] = base_const + const_junk_size;
		counters[2] = base_const + const_junk_size * 2;
		counters[3] = base_const + const_junk_size * 3;
	}
	
	constVariableTable(){ 
	}
	
	int insert(int value){
		constInt tmp;
		tmp.direction = counters[0];
		tmp.value = value;
		constVectorInt.insert(itInt,tmp);
		itInt = constVectorInt.end();	
		counters[0]++;
		return counters[0]-1;	
	}
	
	int insert(float value){
		constFloat tmp;
		tmp.direction = counters[1];
		tmp.value = value;
		constVectorFloat.insert(itFloat,tmp);
		itFloat = constVectorFloat.end();	
		counters[1]++;
		return counters[1]-1;	
	}

	int insert(bool value){
		constBool tmp;
		tmp.direction = counters[2];
		tmp.value = value;
		constVectorBool.insert(itBool,tmp);
		itBool = constVectorBool.end();	
		counters[2]++;
		return counters[2]-1;	
	}

	int insert(char value){
		constChar tmp;
		tmp.direction = counters[3];
		tmp.value = value;
		constVectorChar.insert(itChar,tmp);
		itChar = constVectorChar.end();	
		counters[3]++;
		return counters[3]-1;	
	}

	void saveTable(){
		ofstream nFile;
		nFile.open("ConstantVarTable.txt",ios::out);
		if(!nFile) cerr << "I had problems saving the Constants Table in the file" << endl;
		else{
			constInt tmpInt;
			constFloat tmpFloat;
			constBool tmpBool;
			constChar tmpChar;

			for(itInt=constVectorInt.begin(); itInt<constVectorInt.end(); itInt++){
				tmpInt = *itInt;
				nFile << tmpInt.direction << " " << tmpInt.value << endl;
			}
			for(itFloat=constVectorFloat.begin(); itFloat<constVectorFloat.end(); itFloat++){
				tmpFloat = *itFloat;
				nFile << tmpFloat.direction << " " << tmpFloat.value << endl;
			}
			for(itBool=constVectorBool.begin(); itBool<constVectorBool.end(); itBool++){
				tmpBool = *itBool;
				nFile << tmpBool.direction << " " << tmpBool.value << endl;
			}
			for(itChar=constVectorChar.begin(); itChar<constVectorChar.end(); itChar++){
				tmpChar = *itChar;
				nFile << tmpChar.direction << " " << tmpChar.value << endl;
			}
		}
		nFile.close();
	}
	
	void reconstructConstTable(){
		ifstream nFile;
		constInt tmpInt, tmpInt2;
		constFloat tmpFloat, tmpFloat2;
		constBool tmpBool, tmpBool2;
		constChar tmpChar, tmpChar2;
		int direction;
		
		nFile.open("ConstantVarTable.txt",ios::in);
		
		if(!nFile){ 
			cerr << "I had problems opening the Constants Table file, I'll not be able to start the SinCrema Virtual Machine." << endl;
			exit(1);
		}
		else{
			while(!nFile.eof()){
				nFile >> direction;
				if(direction < baseDirection + junkSize){ 
					nFile >> tmpInt.value;
					tmpInt.direction = direction;
					if(itInt != constVectorInt.begin()){
						tmpInt2 = constVectorInt.back();
						if(tmpInt.direction != tmpInt2.direction) constVectorInt.insert(itInt,tmpInt);
					}
					else constVectorInt.insert(itInt,tmpInt);
					itInt = constVectorInt.end();
				}
				if(direction >= baseDirection + junkSize && direction < baseDirection + 2*junkSize){ 
					nFile >> tmpFloat.value;
					tmpFloat.direction = direction;
					if(itFloat != constVectorFloat.begin()){
						tmpFloat2 = constVectorFloat.back();
						if(tmpFloat.direction != tmpFloat2.direction) constVectorFloat.insert(itFloat,tmpFloat);
					}
					else constVectorFloat.insert(itFloat,tmpFloat);
					itFloat = constVectorFloat.end();
				}
				if(direction >= baseDirection + 2*junkSize && direction < baseDirection + 3*junkSize){ 
					nFile >> tmpBool.value;
					tmpBool.direction = direction;
					if(itBool != constVectorBool.begin()){
						tmpBool2 = constVectorBool.back();
						if(tmpBool.direction != tmpBool2.direction) constVectorBool.insert(itBool,tmpBool);
					}
					else constVectorBool.insert(itBool,tmpBool);
					itBool = constVectorBool.end();
				}
				if(direction >= baseDirection + 3*junkSize){ 
					nFile >> tmpChar.value;
					tmpChar.direction = direction;
					if(itChar != constVectorChar.begin()){
						tmpChar2 = constVectorChar.back();
						if(tmpChar.direction != tmpChar2.direction) constVectorChar.insert(itChar,tmpChar);
					}
					else constVectorChar.insert(itChar,tmpChar);
					itChar = constVectorChar.end();
				}
			}
		}
		nFile.close();

		//display para debug :)
		/*for(itInt=constVectorInt.begin(); itInt<constVectorInt.end(); itInt++){
			tmpInt = *itInt;
			cout << tmpInt.direction << " " << tmpInt.value << endl;
		}
		for(itFloat=constVectorFloat.begin(); itFloat<constVectorFloat.end(); itFloat++){
			tmpFloat = *itFloat;
			cout << tmpFloat.direction << " " << tmpFloat.value << endl;
		}
		for(itBool=constVectorBool.begin(); itBool<constVectorBool.end(); itBool++){
			tmpBool = *itBool;
			cout << tmpBool.direction << " " << tmpBool.value << endl;
		}
		for(itChar=constVectorChar.begin(); itChar<constVectorChar.end(); itChar++){
			tmpChar = *itChar;
			cout << tmpChar.direction << " " << tmpChar.value << endl;
		}*/
	}
	
	vector<int> getConstVectorInt(){
		vector<int> intValuesVector;
		vector<int>::iterator it;
		constInt tmpInt;
		it = intValuesVector.begin();
		for(itInt=constVectorInt.begin(); itInt<constVectorInt.end(); itInt++){
			tmpInt = *itInt;
			intValuesVector.insert(it,tmpInt.value);
			it = intValuesVector.end();
		}
		return intValuesVector;
	}

	vector<float> getConstVectorFloat(){
		vector<float> floatValuesVector;
		vector<float>::iterator it;
		constFloat tmpFloat;
		it = floatValuesVector.begin();
		for(itFloat=constVectorFloat.begin(); itFloat<constVectorFloat.end(); itFloat++){
			tmpFloat = *itFloat;
			floatValuesVector.insert(it,tmpFloat.value);
			it = floatValuesVector.end();
		}
		return floatValuesVector;
	}

	vector<bool> getConstVectorBool(){
		vector<bool> boolValuesVector;
		vector<bool>::iterator it;
		constBool tmpBool;
		it = boolValuesVector.begin();
		for(itBool=constVectorBool.begin(); itBool<constVectorBool.end(); itBool++){
			tmpBool = *itBool;
			boolValuesVector.insert(it,tmpBool.value);
			it = boolValuesVector.end();
		}
		return boolValuesVector;
	}

	vector<char> getConstVectorChar(){
		vector<char> charValuesVector;
		vector<char>::iterator it;
		constChar tmpChar;
		it = charValuesVector.begin();
		for(itChar=constVectorChar.begin(); itChar<constVectorChar.end(); itChar++){
			tmpChar = *itChar;
			charValuesVector.insert(it,tmpChar.value);
			it = charValuesVector.end();
		}
		return charValuesVector;
	}
};

/* class memory
 *
 * Esta clase implementa la memoria que se utilizara por la maquina virtual. Posee
 * un vector por cada tipo de datos soportado por el lenguaje. 
 *
 * Sus contenedores de datos son:
 * intMemory		- un vector que emula la memoria de enteros
 * floatMemory		- un vector que emula la memoria de flotantes
 * booleanMemory	- un vector que emula la memoria de booleanos
 * charMemory		- un vector que emula la memoria de chars
 *
 * Sus metodos son: 
 * memory()	- el constructor de la clase
 * reuse() - Setea los iteradores en el comienzo y vacia los vectores. 
 * insert() - dependiendo del tipo de dato que reciba, lo guarda en la memoria correspondiente
 * displayMem() - despliega la memoria completa, para efectos de debugging
 *
 */

class memory {
	public:
	vector<int> intMemory;
	vector<int>::iterator itInt;
	
	vector<float> floatMemory;
	vector<float>::iterator itFloat;
	
	vector<bool> booleanMemory;
	vector<bool>::iterator itBool;

	vector<char> charMemory;
	vector<char>::iterator itChar;	
	
	memory(){
		itInt = intMemory.begin();
		itFloat = floatMemory.begin();
		itBool = booleanMemory.begin();
		itChar = charMemory.begin();
	}
	
	void reuse(){
		intMemory.clear();
		floatMemory.clear();
		booleanMemory.clear();
		charMemory.clear();
		itInt = intMemory.begin();
		itFloat = floatMemory.begin();
		itBool = booleanMemory.begin();
		itChar = charMemory.begin();
	}
	
	void insert(int value){
		intMemory.insert(itInt,value);
		itInt = intMemory.end();
	}
	
	void insert(float value){
		floatMemory.insert(itFloat,value);
		itFloat = floatMemory.end();
	}
	
	void insert(bool value){
		booleanMemory.insert(itBool,value);
		itBool = booleanMemory.end();
	}
	
	void insert(char value){
		charMemory.insert(itChar,value);
		itChar = charMemory.end();
	}
	
	void displayMem(){
		int tmp;
		cout << "Memoria de enteros" << endl;
		for(itInt=intMemory.begin(); itInt<intMemory.end(); itInt++){
			tmp = *itInt;
			cout << tmp << endl;
		}
		float tmp2;
		cout << "Memoria de flotantes" << endl;
		for(itFloat=floatMemory.begin(); itFloat<floatMemory.end(); itFloat++){
			tmp2 = *itFloat;
			cout << tmp2 << endl;
		}
		bool tmp3;
		cout << "Memoria de booleanos" << endl;
		for(itBool=booleanMemory.begin(); itBool<booleanMemory.end(); itBool++){
			tmp3 = *itBool;
			cout << tmp3 << endl;
		}
		char tmp4;
		cout << "Memoria de chars" << endl;
		for(itChar=charMemory.begin(); itChar<charMemory.end(); itChar++){
			tmp4 = *itChar;
			cout << tmp4 << endl;
		}
	}
};

/* class intermediateCode
 *
 * Esta clase es el contenedor del codigo intermedio generado por el compilador.
 *
 * Sus contenedores de datos son:
 * cuadruplo	- una estructura que almacena el id del cuadruplo a generar, el
 *	  codigo del operador, las direcciones del operando 1 y 2 y la direccion donde
 *	  se guardara el resultado
 * codeVector   - un vector de estructuras cuadruplos
 *
 * Sus metodos son: 
 * intermediateCode()	- el constructor de la clase
 * getCounter() - regresa el proximo id a utilizar
 * insertCuadruplo() - inserta en el vector el cuadruplo que recibe
 * getOperationCode() - regresa el numero de operacion del codigo que reciba como string
 * getCuadruplo() - regresa el cuadruplo que se le pida por id    
 * fillCuadruplo() - rellena un cuadruplo que ya ha sido creado al que le falta informacion
 * saveCuadruplo() - guarda todo el vector de cuadruplos a un archivo de nombre
 *	  QuadruplosTable.txt para ser utilizado por la maquina virtual posteriormente
 * reconstructCuadruplosVector() - reconstruye el vector de cuadruplos a partir del
 *	  archivo QuadruplosTable.txt
 * initIterator() - inicializa al iterador del vector de cuadruplos en el comienzo
 *	  del vector
 * getThisCuadruplo() - retorna una estructura de cuadruplo que se le pida por id 
 *
 */

struct cuadruplo {
		int id;
		int oper;
		int op1;
		int op2;
		int result;		
	};

class intermediateCode {
	vector<cuadruplo> codeVector;
	vector<cuadruplo>::iterator it;
	int counter;

	public:
	intermediateCode() {
		it = codeVector.begin();			
		counter = 1;
	}

	int getCounter() {
		return counter;
	}

	void insertCuadruplo(int oper, int op1, int op2, int result) {
		cuadruplo aux;
		aux.id = counter++;
		aux.oper = oper;
		aux.op1 = op1;
		aux.op2 = op2;
		aux.result = result;
		codeVector.insert(it,aux);
		it = codeVector.end();
	}

	void insertCuadruplo(const wchar_t* oper, int op1, int op2, int result) {
		cuadruplo aux;
		aux.id = counter++;
		aux.oper = getOperationCode(oper);
		aux.op1 = op1;
		aux.op2 = op2;
		aux.result = result;
		codeVector.insert(it,aux);
		it = codeVector.end();
	}

	int getOperationCode(const wchar_t* oper) {
		if (!wcscmp(oper, L"+")){
			return 0;
		} else if (!wcscmp(oper, L"-")) {
			return 1;
		} else if (!wcscmp(oper, L"*")) {
			return 2;
		} else if (!wcscmp(oper, L"/")) {
			return 3;
		} else if (!wcscmp(oper, L">")) {
			return 4;
		} else if (!wcscmp(oper, L"<")) {
			return 5;
		} else if (!wcscmp(oper, L"<>")) {
			return 6;
		} else if (!wcscmp(oper, L":<")) {
			return 7;
		} else if (!wcscmp(oper, L":>")) {
			return 8;
		} else if (!wcscmp(oper, L":{")) {
			return 9;
		} else if (!wcscmp(oper, L"AND")) {
			return 10;
		} else if (!wcscmp(oper, L"OR")) {
			return 11;
		} else if (!wcscmp(oper, L":O")) {
			return 12;
		} else if (!wcscmp(oper, L":D")) {
			return 13;
		} else if (!wcscmp(oper, L"NOT")) {
			return 14;
		} else if (!wcscmp(oper, L"NEG")) {
			return 15;
		} else if (!wcscmp(oper, L"GOTO")) {
			return 16;
		} else if (!wcscmp(oper, L"PRINT")) {
			return 17;
		} else if (!wcscmp(oper, L"READ")) {
			return 18;
		} else if (!wcscmp(oper, L"ERA_MEM")) {
			return 19;
		} else if (!wcscmp(oper, L"ERA_TEMP")) {
			return 20;
		} else if (!wcscmp(oper, L"PARAM")) {
			return 21;
		} else if (!wcscmp(oper, L"ATTRIBUTE")) {
			return 22;
		} else if (!wcscmp(oper, L"GOSUB")) {
			return 23;
		} else if (!wcscmp(oper, L"RTN")) {
			return 24;
		} else if (!wcscmp(oper, L"GO_BACK")) {
			return 25;
		} else if (!wcscmp(oper, L"RETURNS")) {
			return 26;
		} else if (!wcscmp(oper, L"GOTO_F")) {
			return 27;
		}
	}

	cuadruplo getCuadruplo(int id) {
		cuadruplo aux;			
		for (it = codeVector.begin(); it < codeVector.end(); it++) {
			aux = *it;
			if(id == aux.id) {
				it = codeVector.end();
				return aux;
			}
		}
		it = codeVector.end();
		aux.id = 0;
		return aux;
	}
		
	void fillCuadruplo(int id, int value) {
		cuadruplo aux;			
		for (it = codeVector.begin(); it < codeVector.end(); it++) {
			aux = *it;
			if(id == aux.id) {
				it->result = value;
				it = codeVector.end();
				break;
			}
		}		
		it = codeVector.end();	
	}

	void fillCuadruplo(int id, int oper, int op1, int op2, int result) {
		cuadruplo aux;			
		for (it = codeVector.begin(); it < codeVector.end(); it++) {
			aux = *it;
			if(id == aux.id) {
				it->oper = oper;
				it->op1 = op1;
				it->op2 = op2;		
				it->result = result;
				it = codeVector.end();
				break;
			}
		}	
		it = codeVector.end();	
	}

	void saveCuadruplo(){
		ofstream nFile;
		nFile.open("QuadruplosTable.txt",ios::out);
		if(!nFile) cerr << "I had problems saving the Quadruplos Table in the file" << endl;
		else{
			cuadruplo tmp;
			for(it=codeVector.begin(); it<codeVector.end(); it++){
				tmp = *it;
				nFile << tmp.id << " " << tmp.oper << " " << tmp.op1 << " " << tmp.op2 << " " << tmp.result << endl;
			}
		}
		nFile.close();
	}

	void reconstructCuadruplosVector(){
		ifstream nFile;
		cuadruplo tmp;
		
		nFile.open("QuadruplosTable.txt",ios::in);
		
		if(!nFile){ 
			cerr << "I had problems opening the Quadruplos file, I'll not be able to start the SinCrema Virtual Machine." << endl;
			exit(1);
		}
		else{
			it = codeVector.begin();
			while (!nFile.eof()){
				nFile >> tmp.id >> tmp.oper >> tmp.op1 >> tmp.op2 >> tmp.result;
				codeVector.insert(it,tmp);
				it = codeVector.end();
			}
		}
	}

	void initIterator(){
		it = codeVector.begin();
	}

	cuadruplo getThisCuadruplo(int which){
		if(which > codeVector.size()-1){
		cuadruplo tmp;
		tmp.id = -1;
		return tmp;
		}
		else return codeVector[which - 1];
	}

};

/* class variablesTable
 *
 * Esta clase implementa la tabla de variables. Una tabla de variables guarda la 
 * informacion de las variables de cada método o de cada entorno global de cada 
 * clase.  
 *
 * Sus contenedores de datos son:
 * variableInfo	- una estructura que guarda la informacion sobre una variable.
 *	  Guarda el nombre de la variable en id, el tipo en type, el acceso en access,
 *	  la direccion de memoria en memoryDirection y un puntero a un objeto de tipo.
 * variableVector - Es el vector que guarda las estructuras variableInfo, es lo
 *	  que funciona como la tabla en este caso para ir indexando mas renglones por
 *    cada variable.    
 *
 * Sus metodos son: 
 * variablesTable()	- el constructor de la clase. Recibe un entero factor, el cual
 *	  sirve de contador para saber dentro de qué memoria se guardaran sus variables
 *	  en la maquina virtual. Se maneja un numero distinto para cada entrada en la
 *	  tabla de variables y este factor sirve para calcular la direccion de memoria.
 *	  También recibe el numero de la memoria base y el tamano de memoria, para poder
 *	  hacer los calculos de las direcciones de memoria correctamente.
 * exist() - retorna true si el id por el que se le pregunto existe, y false de lo
 *	  contrario. Con esto se comprueba unicidad y existencia.
 * insertVariable() - se le envia el id, el acceso y el tipo y este los guarda en
 *	  una estructura variableInfo y luego lo inserta en el vector. El segundo 
 *	  metodo sobrecargado que existe, recibe adicionalmente un entero con la direccion
 *	  de memoria.
 * insert() - inserta la estructura de tipo variableInfo que recibe en el vector.
 * insertObject() - similar al metodo insertVariable, con la diferencia que este
 *	  recibe también el puntero 'object'.
 * getElement() - retorna la estructura de tipo variableInfo del id que se le pida.
 * displayVars() - imprime todo el contenido del vector, estructura a estructura
 *	  guardada, para efectos de debbugging.
 * insertIntoTable() - copia valores de una tabla a otra con direcciones de memoria
 *	  nuevas.
 * setDirection() - modifica el valor de la direccion de una variable.
 * getFirst() - obtiene la primer variable de la tabla.
 * getLast() - obtiene la última variable de la tabla.
 * isEmpty() - retorna true si esta vacia.
 * getDirections() - regresa una fila con las direcciones de todas las variables
 *	  de la tabla.
 */

class variablesTable{
	public:
	struct variableInfo{
		const wchar_t* id;
		const wchar_t* type;
		const wchar_t* access;
		int memoryDirection;
		variablesTable *object;
	};

	vector<variableInfo> variableVector;
	vector<variableInfo>::iterator it;

	int contInt;
	int contFloat;
	int contBoolet;
	int contChar;
	int procFactor;

	variablesTable(int factor, int base_mem, int junk_size){
		procFactor = factor;
		it = variableVector.begin(); 
		int base = base_mem + (factor * (4 * junk_size));
		contInt = base;
		contFloat = base + junk_size;
		contBoolet = base + (junk_size * 2);
		contChar = base + (junk_size * 3);
	}
		
	variablesTable(){
		it = variableVector.begin(); 
	}
		variablesTable(int cInt, int cFloat, int cBoolet, int cChar) {
		it = variableVector.begin();
		contInt = cInt;
		contFloat = cFloat;
		contBoolet = cBoolet;
		contChar = cChar;
	}
		
	bool exist(const wchar_t* id){
		variableInfo temp;
		for(it=variableVector.begin(); it<variableVector.end(); it++){
			temp = *it;
			if(!wcscmp(id, temp.id)) {
				it = variableVector.end();
				return true; 
			}
		}
		return false;
	}

	void insertVariable(const wchar_t* id, const wchar_t* type, const wchar_t* access){
		variableInfo newVariable;
		newVariable.id = id;
		newVariable.type = type;
		newVariable.access = access;
		newVariable.object = 0;
		if (!wcscmp(type, L"int")) {
			newVariable.memoryDirection = contInt++;
		} else if (!wcscmp(type, L"float")) {
			newVariable.memoryDirection = contFloat++;
		} else if (!wcscmp(type, L"boolet")) {
			newVariable.memoryDirection = contBoolet++;
		} else if (!wcscmp(type, L"char")) {
			newVariable.memoryDirection = contChar++;
		}
		variableVector.insert(it,newVariable);
		it = variableVector.end();
	}
	
	void insert(variableInfo var) {
		variableVector.insert(it, var);
		it = variableVector.end();
	}

	void insertVariable(const wchar_t* id, const wchar_t* type, const wchar_t* access, int memory){
		variableInfo newVariable;
		newVariable.id = id;
		newVariable.type = type;
		newVariable.access = access;
		newVariable.memoryDirection = memory;
		newVariable.object = 0;
		variableVector.insert(it,newVariable);
		it = variableVector.end();
	}

	void insertObject(const wchar_t* id, const wchar_t* type, const wchar_t* access, variablesTable &object){
		variableInfo newVariable;
		newVariable.id = id;
		newVariable.type = type;
		newVariable.access = access;
		newVariable.memoryDirection = -1;
		newVariable.object = &object;

		contInt = object.contInt;
		contFloat = object.contFloat;
		contBoolet = object.contBoolet;
		contChar = object.contChar;

		variableVector.insert(it,newVariable);
		it = variableVector.end();
	}

	variableInfo getElement(const wchar_t* id) {
		variableInfo temp;
		for(it=variableVector.begin(); it<variableVector.end(); it++){
			temp = *it;
			if( !wcscmp(temp.id,id)) {
				it = variableVector.end();
				return temp; 
			}
		}
		temp.id = L"";
		temp.type = L"";
		temp.access = L"";
		temp.memoryDirection = -1;
		return temp;
	}
	
	void displayVars() {
		wcout << "Variables Table\n";
		variableInfo newVariable;
		for(it=variableVector.begin(); it<variableVector.end(); it++){
			wcout << "<>\n";
			newVariable = *it;
			wcout<< L"id: " << newVariable.id << L'\n';
			wcout << L"type: " << newVariable.type << L'\n';
			wcout << L"access: " << newVariable.access << L'\n';
			wcout << L"memory direction: " << newVariable.memoryDirection << L'\n';
			if(newVariable.object != 0) {
				wcout << newVariable.type << L"\n";
				wcout << L">\n";
				newVariable.object->displayVars();
				wcout << L">\n";
			} 
			wcout << "<>\n";	
		}
	}

	void insertIntoTable(variablesTable *table) {
		variableInfo temp;
		for(it = variableVector.begin(); it < variableVector.end(); it++) {
			temp = *it;
			const wchar_t* tId = temp.id;
			const wchar_t* tType = temp.type;
			const wchar_t* tAccess = temp.access;

			if (temp.object != 0) {
				variablesTable *tObject = new variablesTable(contInt, contFloat, contBoolet, contChar);
				temp.object->insertIntoTable(tObject);
				table->insertObject(tId, tType, tAccess, *tObject);
			} else {
				table->insertVariable(tId, tType, tAccess);
			}
		}
	}

	void setDirection(const wchar_t* id, int direction) {
		variableInfo temp;
		for(it=variableVector.begin(); it<variableVector.end(); it++){
			temp = *it;
			if(!wcscmp(temp.id,id)) {
				it->memoryDirection = direction;
				it = variableVector.end();
				break;
			}
		}
	}

	variableInfo getFirst() {
		it = variableVector.begin();
		variableInfo aux = *it;
		it = variableVector.end();
		return aux;
	}

	variableInfo getLast() {
		return variableVector.back();
	}		

	bool isEmpty() {
		return variableVector.empty();
	}
	
	queue<int> getDirections() {
		variableInfo temp;
		queue<int> aux;
		for(it = variableVector.begin(); it < variableVector.end(); it++) {
			temp = *it;
			if (wcscmp(temp.id,L"returnInt") && wcscmp(temp.id,L"returnFloat") && wcscmp(temp.id,L"returnBoolet") && wcscmp(temp.id,L"returnChar") && wcscmp(temp.id,L"returnObject") ) {
				if (temp.object != 0) {
					variablesTable *table = temp.object;
					queue<int> aux1 = table->getDirections();
					while(!aux1.isEmpty()) {
						aux.enqueue(aux1.dequeue());
					}
				} else {
					aux.enqueue(temp.memoryDirection);
				}
			}
		}
		return aux;
	}

	queue<const wchar_t*> getTypes() {
		variableInfo temp;
		queue<const wchar_t*> aux;
		for(it = variableVector.begin(); it < variableVector.end(); it++) {
			temp = *it;
			if (wcscmp(temp.id,L"returnInt") && wcscmp(temp.id,L"returnFloat") && wcscmp(temp.id,L"returnBoolet") && wcscmp(temp.id,L"returnChar") && wcscmp(temp.id,L"returnObject") ) {
				if (temp.object != 0) {
					variablesTable *table = temp.object;
					queue<const wchar_t *> aux1 = table->getTypes();
					while(!aux1.isEmpty()) {
						aux.enqueue(aux1.dequeue());
					}
				} else {
					aux.enqueue(temp.type);
				}
			}
		}
		return aux;
	}

	variableInfo getElement(int num) {
		variableInfo aux;
		int i = 0;
		for(it=variableVector.begin(); it < variableVector.end(); it++) {
			if (i == num) {
				aux = *it;
				it = variableVector.end();
				return aux;
			}
			i++;
		}
		it = variableVector.end();
		aux.id = L"";
		return aux;
	}
};

/* class procedureDirectory
 *
 * Esta clase implementa el directorio de procedimientos de una clase.
 *
 * Sus contenedores de datos son:
 * procedureInfo	- guarda el id, el tipo de acceso, un booleanos para saber si
 *	  es estatico o no, el tipo de dato que retorna, una lista de tipos de parametros,
 *	  el tipo del procedimiento (constructor, global o procedimiento), un puntero
 *	  a su tabla de variables y el numero del cuadruplo desde donde empieza el 
 *	  procedimiento.
 * procedureVector  - este vector de procedureInfo emula la tabla del directorio 
 *	  de procedimientos.
 *
 * Sus metodos son: 
 * procedureDirectory()	- constructor de la clase.
 * exist() - verifica si existe el procedimiento del tipo especificado en el directorio.
 * insert() - inserta un procedimiento.
 * insertProcedure() - inserta un procedimiento con parametros especificos.
 * getElement() - regresa un procedimiento dado un identificador.
 * insertIntoDir() - copia un directorio de procedimientos a otro con direcciones
 *	  nuevas.
 * displayDir() - despliega el directorio de procedimientos.
 * getVarType() - busca el tipo de datos de una variable dado un procedimiento, de 
 *	  no encontrarlo, busca en el procedimiento global.
 * getVarLocation() - busca la tabla de variables de un objeto dado un procedimiento, de 
 *	  no encontrarlo, busca en el procedimiento global.
 * getCuadruplo() - regresa el siguiente numero del cuadruplo dado un procedimiento.
 * setCuadruplo() - asigna el siguiente cuadruplo a ejecutar dado un procedimiento.
 * setLastCuadruplo() - asigna el valor del ultimo cuadruplo.
 * getLast() - regresa el ultimo procedimiento del directorio.
 */

struct procedureInfo{
	const wchar_t* id;
	const wchar_t* access;
	bool isStatic;
	const wchar_t* returns;
	queue<const wchar_t*> *parametersTypes;
	const wchar_t* whatKind;		//constructor | procedure  | global
	variablesTable *varTable;
	int cuadruplo;
};

class procedureDirectory{

	vector<procedureInfo> procedureVector;
	vector<procedureInfo>::iterator it;

	public:
	procedureDirectory(){
		it = procedureVector.begin();
	}
	
	bool exist(const wchar_t* id, const wchar_t* kind){
		procedureInfo temp;
		for(it=procedureVector.begin(); it<procedureVector.end(); it++){
			temp = *it;
			if(!wcscmp(temp.id,id)) {
				if (!wcscmp(temp.whatKind, kind)) {
					it = procedureVector.end();
					return true; 
				}
			}
		}
		return false;
	}
	
	void insert(procedureInfo newProcedure) {
		procedureVector.insert(it, newProcedure);
		it = procedureVector.end();
	}

	void insertProcedure(const wchar_t* id, const wchar_t* access, bool isStatic, const wchar_t* returns, queue<const wchar_t*> &parametersTypes, const wchar_t* whatKind, variablesTable &varTable){
		procedureInfo newProcedure;
		newProcedure.id = id;
		newProcedure.access = access;
		newProcedure.isStatic = isStatic;
		newProcedure.returns = returns;
		newProcedure.parametersTypes = &parametersTypes;
		newProcedure.whatKind = whatKind;
		newProcedure.varTable = &varTable;
		procedureVector.insert(it,newProcedure);
		it = procedureVector.end();
		//it++;
	}

	void insertProcedure(const wchar_t* id, const wchar_t* access, bool isStatic, const wchar_t* returns, queue<const wchar_t*> &parametersTypes, const wchar_t* whatKind, variablesTable &varTable, int cuadruplo){
		procedureInfo newProcedure;
		newProcedure.id = id;
		newProcedure.access = access;
		newProcedure.isStatic = isStatic;
		newProcedure.returns = returns;
		newProcedure.parametersTypes = &parametersTypes;
		newProcedure.whatKind = whatKind;
		newProcedure.varTable = &varTable;
		newProcedure.cuadruplo = cuadruplo;
		procedureVector.insert(it,newProcedure);
		it = procedureVector.end();
		//it++;
	}

	procedureInfo getElement(const wchar_t* id) {
		procedureInfo temp;
		for(it=procedureVector.begin(); it<procedureVector.end(); it++){
			temp = *it;
			if(!wcscmp(temp.id,id)) {
				it = procedureVector.end();
				return temp; 
			}
		}
		temp.id = L"";
		temp.access = L"";
		temp.isStatic = false;
		temp.returns = L"";
		queue<const wchar_t*> tQueue;
		temp.parametersTypes = &tQueue;
		temp.whatKind = L"";
		variablesTable tTable = variablesTable();
		temp.varTable = &tTable;
		return temp;
	}

	void insertIntoDir(procedureDirectory *dir) {
		procedureInfo temp;
		for(it=procedureVector.begin(); it<procedureVector.end(); it++){
			temp = *it;
			const wchar_t* tId = temp.id;
			const wchar_t* tAccess = temp.access;
			bool tIsStatic = temp.isStatic;
			const wchar_t* tReturns = temp.returns;
			queue<const wchar_t*>* tQueue = new queue<const wchar_t*>;
			temp.parametersTypes->insertIntoQueue(tQueue);
			const wchar_t* tWhatKind = temp.whatKind;
			variablesTable* tTable = new variablesTable();
			temp.varTable->insertIntoTable(tTable);

			dir->insertProcedure(tId, tAccess, tIsStatic, tReturns, *tQueue, tWhatKind, *tTable);
		}
	}

	void displayDir() {
		wcout << "Procedure Directory: \n";
		procedureInfo temp;
		for(it=procedureVector.begin(); it<procedureVector.end(); it++){
			wcout << "<============>\n";
			temp = *it;
			wcout << L"id: " << temp.id << L'\n';
			wcout << L"access: " << temp.access << L'\n';
			wcout << L"static?: " << temp.isStatic << L'\n';
			wcout << L"returns: " << temp.returns << L'\n';
			wcout << L"type: " << temp.whatKind << L'\n';
			temp.varTable->displayVars();
		}
		wcout << "<============>\n";
	}

	const wchar_t* getVarType(const wchar_t* id, const wchar_t* dir) {
		variablesTable *table = getElement(dir).varTable;
		const wchar_t* aux = table->getElement(id).type;
		if (wcscmp(aux,L"")) {
			return aux;
		} else if (wcscmp(dir,L"global")) {
			table = getElement(L"global").varTable;
			const wchar_t* aux2 = table->getElement(id).type;
			if (wcscmp(aux2,L"")) {
				const wchar_t* access = table->getElement(id).access;
				if (!wcscmp(access, L"public")) { 
					return aux2;
				}
			}
		}
		return L"";
	}

	variablesTable* getVarObject(const wchar_t* id, const wchar_t* dir) {
		variablesTable *table = getElement(dir).varTable;
		const wchar_t* aux = table->getElement(id).type;
		if (wcscmp(aux,L"")) {
			return table->getElement(id).object;
		} else if (wcscmp(dir,L"global")) {
			table = getElement(L"global").varTable;
			const wchar_t* aux2 = table->getElement(id).type;
			if (wcscmp(aux2,L"")) {
				const wchar_t* access = table->getElement(id).access;
				if (!wcscmp(access, L"public")) { 
					return table->getElement(id).object;
				}
			}
		}
		return 0;
	}


	int getVarLocation(const wchar_t* id, const wchar_t* dir) {
		variablesTable *table = getElement(dir).varTable;
		int aux = table->getElement(id).memoryDirection;
		if (aux >= 0) {
			return aux;
		} else if (wcscmp(dir,L"global")) {
			table = getElement(L"global").varTable;
			aux = table->getElement(id).memoryDirection;
			if (aux >= 0) {
				if (!wcscmp(table->getElement(id).access,L"public")) {
					return aux;
				}
			}
		}
		return -1;
	}
		
	int getCuadruplo(const wchar_t* id) {
		return getElement(id).cuadruplo;
	}

	void setCuadruplo(const wchar_t* id, int value) {
		procedureInfo temp;
		for(it=procedureVector.begin(); it<procedureVector.end(); it++){
			temp = *it;
			if(!wcscmp(temp.id,id)) {
				it->cuadruplo = value;
				break;
			}
		}
	} 

	void setLastCuadruplo(int value) {
		--it->cuadruplo = value;
		it = procedureVector.end();
	}

	procedureInfo getLast() {
		return procedureVector.back();
	}
};


/* class classDirectory
 *
 * Esta clase implementa el directorio de clases del compilador.
 *
 * Sus contenedores de datos son:
 * classInfo	- guarda su id, fatherId el id del padre y un puntero a su 
 *	  directorio de procedimientos.
 * classVector  - es el vector que simula el directorio de clases (la estructura
 *	  de tabla).
 *
 * Sus metodos son: 
 * classDirectory()	- constructor de la clase. 
 * exist() - verifica que exista una clase dado un id y dada la columna donde 
 *	  buscará
 * insertClass() - agrega una clase al directorio de clases.
 * getElement() - regresa una clase dado un identificador.
 * display() - despliega todo el directorio de clases.
 *
 */


class classDirectory{
	struct classInfo{
		const wchar_t* id;
		const wchar_t* fatherId;
		procedureDirectory *procDir;
	};
	vector<classInfo> classVector;
	vector<classInfo>::iterator it;
	
	public:
	classDirectory(){
		it = classVector.begin();
	}
		
	bool exist(const wchar_t* id, const wchar_t* where){ // where = id | fatherId
		classInfo temp;
		for(it=classVector.begin(); it<classVector.end(); it++){
			temp = *it;
			if(!wcscmp(where,L"id") && !wcscmp(temp.id,id)) {
				it = classVector.end();
				return true; 
			}
			if(!wcscmp(where,L"fatherId") && !wcscmp(temp.fatherId,id)) {
				it = classVector.end();
				return true;
			}
		}
		return false;
	}
		
	void insertClass(const wchar_t* id, const wchar_t* fatherId, procedureDirectory &procDir){
		classInfo newClass;
		newClass.id = id;
		newClass.fatherId = fatherId;
		newClass.procDir = &procDir;
		classVector.insert(it,newClass);
		it = classVector.end();
		//it++;
	}

	classInfo getElement(const wchar_t* id) {
		classInfo temp;
		for(it=classVector.begin(); it<classVector.end(); it++){
			temp = *it;
			if(!wcscmp(temp.id,id)) {
				it = classVector.end();
				return temp; 
			}
		}
		temp.id = L"";
		temp.fatherId = L"";
		procedureDirectory tDir = procedureDirectory();
		temp.procDir = &tDir;
		return temp;
	}
	
	void display() {
		classInfo temp;
		wcout << "Directorio de Clases\n";
		for(it=classVector.begin(); it<classVector.end(); it++) {
			wcout << "<============>\n";
			wcout << "<============>\n";
			temp = *it;
			wcout << L"id: " << temp.id << L'\n';
			wcout << L"fatherId: " << temp.fatherId << L'\n';
			temp.procDir->displayDir();
		}
		wcout << "<============>\n";
		wcout << "<============>\n";
	}
};
